package com.example.chat.service.Impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.chat.domain.entity.*;
import com.example.chat.domain.vo.MomentCommentVo;
import com.example.chat.domain.vo.QueryPublicMomentVo;
import com.example.chat.domain.vo.QueryScrollMomentVo;
import com.example.chat.domain.vo.UserNameVo;
import com.example.chat.mapper.MomentCommentMapper;
import com.example.chat.mapper.MomentMapper;
import com.example.chat.result.Result;
import com.example.chat.service.MomentCommentService;
import com.example.chat.service.MomentService;
import com.example.chat.service.UserService;
import com.example.chat.utils.RedisConstants;
import com.example.chat.utils.UserHolder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class MomentServiceImpl extends ServiceImpl<MomentMapper, Moment> implements MomentService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private MomentMapper momentMapper;
    @Resource
    private UserService userService;
    @Resource
    private MomentCommentService momentCommentService;
    @Resource
    private MomentCommentMapper momentCommentMapper;

    private static final DefaultRedisScript<Void> add = new DefaultRedisScript<>();

    static {
        add.setLocation(new ClassPathResource("scripts/moment/addMoment.lua"));
        add.setResultType(Void.class);
    }

    @Override
    public Result queryPublicMoment(Long timestamp, Integer offset) {
        Long currentId = UserHolder.getCurrentId();
        QueryPublicMomentVo queryPublicMomentVo = new QueryPublicMomentVo();
        Integer queryCount = 5;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(RedisConstants.MOMENT_PUBLIC_MOMENT + currentId,
                        0, timestamp, offset, queryCount);
        if(typedTuples == null || typedTuples.isEmpty()){
            queryPublicMomentVo.setRemain(false);
            return Result.ok();
        }
        List<Long> momentIds = new ArrayList<>();
        Long tempTimastamp = 0L;
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            momentIds.add(Long.valueOf(typedTuple.getValue()));
            tempTimastamp = typedTuple.getScore().longValue();
        }
        //设置最小时间戳,和是否还有剩余
        queryPublicMomentVo.setRemain(queryCount.equals(momentIds.size()));
        queryPublicMomentVo.setTimestamp(tempTimastamp);

        List<PublicMoment> publicMoments = momentMapper.queryPublicMoment(momentIds);
        List<MomentCommentVo> comments = momentCommentMapper.queryComments(momentIds);
        Map<Long, List<MomentCommentVo>> collect = comments.stream()
                .collect(Collectors.groupingBy(MomentCommentVo::getMomentId));
        for (PublicMoment publicMoment : publicMoments) {
            //将字符串类型转List并set进Imgs中
            List<String> list = JSONUtil.toList(publicMoment.getImageUrls(), String.class);
            publicMoment.setImgs(list);
            publicMoment.setImageUrls("");
            //查询点赞列表
            Set<String> idStrs = stringRedisTemplate.opsForZSet()
                    .reverseRange(RedisConstants.MOMENT_LIKES + publicMoment.getId(), 0, -1);
            if (idStrs != null && idStrs.size() > 0) {
                if(idStrs.contains(currentId.toString())) publicMoment.setLiked(true);
                List<Long> ids = idStrs.stream().map(Long::valueOf).collect(Collectors.toList());
                String join = StrUtil.join(",", ids);
                List<User> users = userService.lambdaQuery().in(User::getId, ids)
                        .last("order by field(id," + join + ")").list();
                List<UserNameVo> userNameVos = BeanUtil.copyToList(users, UserNameVo.class);
                publicMoment.setLikes(userNameVos);
            }
            //查询对应朋友圈评论列表并setComments
            List<MomentCommentVo> momentCommentVos = collect.get(publicMoment.getId());
            if(momentCommentVos == null || momentCommentVos.size() < 1){
                publicMoment.setComments(Collections.EMPTY_LIST);
            }
            publicMoment.setComments(momentCommentVos);
        }
        queryPublicMomentVo.setMoments(publicMoments);
        return Result.ok(queryPublicMomentVo);
    }

    @Override
    public Result likes(Long id,Integer type) {
        Long currentId = UserHolder.getCurrentId();
        if(type.equals(1)){
            Boolean add = stringRedisTemplate.opsForZSet()
                    .add(RedisConstants.MOMENT_LIKES + id, currentId.toString(), System.currentTimeMillis());
            if(add){
                lambdaUpdate().eq(Moment::getId,id)
                        .setSql("likes = likes + 1")
                        .update();
            }
        }else {
            Long remove = stringRedisTemplate.opsForZSet().remove(RedisConstants.MOMENT_LIKES + id, currentId.toString());
            if(remove > 0){
                lambdaUpdate().eq(Moment::getId,id)
                        .setSql("likes = likes - 1")
                        .update();
            }
        }
        return Result.ok();
    }

    @Override
    public Result comments(Long id, String text) {
        Long currentId = UserHolder.getCurrentId();
        MomentComment comment = MomentComment.builder()
                .text(text)
                .userId(currentId)
                .momentsId(id)
                .build();
        boolean save = momentCommentService.save(comment);
        if(!save){
            return Result.fail("添加失败");
        }
        return Result.ok();
    }

    @Override
    public Result queryPrivate(Long id,Long timestamp, Integer offset) {
        Long currentId = UserHolder.getCurrentId();
        QueryPublicMomentVo queryPublicMomentVo = new QueryPublicMomentVo();
        Integer queryCount = 3;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(RedisConstants.MOMENT_MY_COMMENT + id,
                        0, timestamp, offset, queryCount);
        if(typedTuples == null || typedTuples.isEmpty()){
            queryPublicMomentVo.setRemain(false);
            return Result.ok();
        }
        List<Long> momentIds = new ArrayList<>();

        User user = userService.getById(id);//获取用户信息
        Long tempTimastamp = 0L;
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            momentIds.add(Long.valueOf(typedTuple.getValue()));
            tempTimastamp = typedTuple.getScore().longValue();
        }
        //设置最小时间戳,和是否还有剩余
        queryPublicMomentVo.setRemain(queryCount.equals(momentIds.size()));
        queryPublicMomentVo.setTimestamp(tempTimastamp);

        List<Moment> moments = lambdaQuery().in(Moment::getId, momentIds).orderByDesc(Moment::getTime).list();
        List<PublicMoment> publicMoments = BeanUtil.copyToList(moments, PublicMoment.class);
        //获取moment对应评论信息
        List<MomentCommentVo> comments = momentCommentMapper.queryComments(momentIds);
        Map<Long, List<MomentCommentVo>> collect = comments.stream()
                .collect(Collectors.groupingBy(MomentCommentVo::getMomentId));
        for (PublicMoment publicMoment : publicMoments) {
            //将字符串类型转List并set进Imgs中
            List<String> list = JSONUtil.toList(publicMoment.getImageUrls(), String.class);
            publicMoment.setImgs(list);
            publicMoment.setImageUrls("");
            publicMoment.setIcon(user.getIcon());
            publicMoment.setUserName(user.getUserName());

            //查询点赞列表并设置likeCount
            Set<String> idStrs = stringRedisTemplate.opsForZSet()
                    .reverseRange(RedisConstants.MOMENT_LIKES + publicMoment.getId(), 0, -1);
            if(idStrs != null && idStrs.size() > 0){
                if(idStrs.contains(currentId.toString())) publicMoment.setLiked(true);
//                publicMoment.setLikeCount(idStrs.size());
            }

            //查询对应朋友圈评论列表并设置commentCount
            List<MomentCommentVo> momentCommentVos = collect.get(publicMoment.getId());
            if(momentCommentVos == null || momentCommentVos.size() < 1){
                publicMoment.setComments(Collections.emptyList());
            }else {
                publicMoment.setCommentCount(momentCommentVos.size());
                publicMoment.setComments(momentCommentVos);
            }
        }
        queryPublicMomentVo.setMoments(publicMoments);

        return Result.ok(queryPublicMomentVo);
    }

    @Override
    public Result getCommentById(Long momentId) {
        Long currentId = UserHolder.getCurrentId();
        PublicMoment publicMoment1 = momentMapper.queryOneMoment(momentId);
        List<MomentCommentVo> comments = momentCommentMapper.queryComments(List.of(momentId));
        Map<Long, List<MomentCommentVo>> collect = comments.stream()
                .collect(Collectors.groupingBy(MomentCommentVo::getMomentId));

        List<String> list1 = JSONUtil.toList(publicMoment1.getImageUrls(), String.class);
        publicMoment1.setImgs(list1);
        publicMoment1.setImageUrls("");

        User user = userService.getById(publicMoment1.getUserId());//获取用户信息
        publicMoment1.setIcon(user.getIcon());
        publicMoment1.setUserName(user.getUserName());

        //查询点赞列表并设置likeCount
        Set<String> idStrs = stringRedisTemplate.opsForZSet()
                .reverseRange(RedisConstants.MOMENT_LIKES + publicMoment1.getId(), 0, -1);
        if(idStrs != null && idStrs.size() > 0){
            if(idStrs.contains(currentId.toString())) publicMoment1.setLiked(true);
            publicMoment1.setLikeCount(idStrs.size());
        }

        //查询对应朋友圈评论列表并设置commentCount
        List<MomentCommentVo> momentCommentVos = collect.get(publicMoment1.getId());
        if(momentCommentVos == null || momentCommentVos.size() < 1){
            publicMoment1.setComments(Collections.emptyList());
        }else {
            publicMoment1.setCommentCount(momentCommentVos.size());
            publicMoment1.setComments(momentCommentVos);
        }
        return Result.ok(publicMoment1);
    }

    @Override
    public Result addMoment(String content, List<String> imgs, Integer scope) {
        Long currentId = UserHolder.getCurrentId();
        Moment moment = Moment.builder()
                .imageUrls(JSONUtil.toJsonStr(imgs))
                .context(content)
                .userId(currentId)
                .visibility(scope)
                .build();
        boolean save = save(moment);
        if(!save){
            return Result.fail("添加失败");
        }
        Long momentId = moment.getId();
        Set<String> set = stringRedisTemplate.opsForSet().members(RedisConstants.ADDRESSLIST_FRIENDS + currentId);
        List<String> ids = set.stream().map(String::valueOf).collect(Collectors.toList());

        List<String> args = List.of(currentId.toString(),momentId.toString(),String.valueOf(System.currentTimeMillis()));
        stringRedisTemplate.execute(add,ids,args.toArray());
        return Result.ok();
    }

//    @Override
//    public Result queryUserMoment(Long id,Long momentId,Integer type) {
//        Long currentId = UserHolder.getCurrentId();
//        QueryScrollMomentVo queryScrollMomentVo = new QueryScrollMomentVo();
//        Integer count = type == 2 ? 10 : 7;
//
//        List<PublicMoment> publicMoments = momentMapper.scrollQuery(id, momentId,count,type);
//        if(publicMoments == null || publicMoments.size() <= 0){
//            queryScrollMomentVo.setRemain(false);
//            queryScrollMomentVo.setMoments(Collections.EMPTY_LIST);
//            return Result.ok(queryScrollMomentVo);
//        }
//        //取出momentIds方便后面一次性查询
//        List<Long> momentIds = publicMoments.stream().map(PublicMoment::getId).collect(Collectors.toList());
//
//        //获取moment对应评论信息
//        List<MomentCommentVo> comments = momentCommentMapper.queryComments(momentIds);
//        Map<Long, List<MomentCommentVo>> collect = comments.stream()
//                .collect(Collectors.groupingBy(MomentCommentVo::getMomentId));
//        for (PublicMoment publicMoment : publicMoments) {
//            //将字符串类型转List并set进Imgs中
//            List<String> list = JSONUtil.toList(publicMoment.getImageUrls(), String.class);
//            publicMoment.setImgs(list);
//            publicMoment.setImageUrls("");
//            //查询点赞列表
//            Set<String> idStrs = stringRedisTemplate.opsForZSet()
//                    .reverseRange(RedisConstants.MOMENT_LIKES + publicMoment.getId(), 0, -1);
//            if(idStrs != null && idStrs.size() > 0){
//                publicMoment.setLiked(idStrs.contains(currentId.toString()));
//                publicMoment.setLikeCount(idStrs.size());
//            }
//
//            //查询对应朋友圈评论列表并设置commentCount
//            List<MomentCommentVo> momentCommentVos = collect.get(publicMoment.getId());
//            if(momentCommentVos == null || momentCommentVos.size() < 1){
//                publicMoment.setComments(Collections.emptyList());
//            }else {
//                publicMoment.setCommentCount(momentCommentVos.size());
//                publicMoment.setComments(momentCommentVos);
//            }
//            queryScrollMomentVo.setMomentId(publicMoment.getId());
//        }
//        queryScrollMomentVo.setMoments(publicMoments);
//        queryScrollMomentVo.setRemain(!(queryScrollMomentVo.getMoments().size() < count));
//        return Result.ok(queryScrollMomentVo);
//    }

    @Override
    public Result queryUserMoment(Long id,Long momentId,Integer type) {
        Long currentId = UserHolder.getCurrentId();
        QueryScrollMomentVo queryScrollMomentVo = new QueryScrollMomentVo();
        Integer count = type == 2 ? 10 : 7;
        Integer offset = momentId == -1 ? 0 : 1;
        momentId = momentId == -1 ? System.currentTimeMillis() : momentId;

        List<PublicMoment> publicMoments;
        //如果id == currentId表示用户自己查询自己，则调用scrollQueryForSelf查询额外的隐藏部分，且不进行缓存
        if(id != null && id == currentId){
           publicMoments = momentMapper.scrollQueryForSelf(id, momentId, count, type);
            if(publicMoments == null || publicMoments.size() <= 0){
                queryScrollMomentVo.setRemain(false);
                queryScrollMomentVo.setMoments(Collections.EMPTY_LIST);
                return Result.ok(queryScrollMomentVo);
            }
        }else{
            //否则表示其他用户查询，调用scrollQuery或缓存里的（不包括隐藏部分）
            Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                    .reverseRangeByScoreWithScores(RedisConstants.MOMENT_USERID + id + ":" + type,
                            0, momentId, offset, count);

            if(typedTuples == null || typedTuples.isEmpty()){
                //重建缓存
                if(id != null){
                    buildMomentRedis(id,type);
                }
                publicMoments = momentMapper.scrollQuery(id, momentId,count,type);
                if(publicMoments == null || publicMoments.size() <= 0){
                    queryScrollMomentVo.setRemain(false);
                    queryScrollMomentVo.setMoments(Collections.EMPTY_LIST);
                    return Result.ok(queryScrollMomentVo);
                }
            }else{
                publicMoments = new ArrayList<>();
                for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
                    PublicMomentHash publicMomentHash =
                            JSONUtil.toBean(typedTuple.getValue(), PublicMomentHash.class);
                    publicMoments.add(BeanUtil.toBean(publicMomentHash,PublicMoment.class));
                }
            }
        }

        //取出momentIds方便后面一次性查询
        List<Long> momentIds = publicMoments.stream().map(PublicMoment::getId).collect(Collectors.toList());
        //获取moment对应评论数
        List<Integer> list1 = momentCommentMapper.QueryCommentsByIds(momentIds);
        for (int i = 0; i < publicMoments.size(); i++)
        {
            PublicMoment publicMoment = publicMoments.get(i);
            //将字符串类型转List并set进Imgs中
            List<String> list = JSONUtil.toList(publicMoment.getImageUrls(), String.class);
            publicMoment.setImgs(list);
            publicMoment.setImageUrls("");
            //查询点赞列表
            Set<String> idStrs = stringRedisTemplate.opsForZSet()
                    .reverseRange(RedisConstants.MOMENT_LIKES + publicMoment.getId(), 0, -1);
            if(idStrs != null && idStrs.size() > 0){
                publicMoment.setLiked(idStrs.contains(currentId.toString()));
                publicMoment.setLikeCount(idStrs.size());
            }
            //查询对应朋友圈评论列表并设置commentCount
            publicMoment.setCommentCount(list1.get(i));
            queryScrollMomentVo.setMomentId(publicMoment.getId());
        }
        queryScrollMomentVo.setMoments(publicMoments);
        queryScrollMomentVo.setRemain(!(queryScrollMomentVo.getMoments().size() < count));
        return Result.ok(queryScrollMomentVo);
    }

    @Override
    public Result addMomentByType(String content, List<String> imgs, String file, Integer type) {
        Long currentId = UserHolder.getCurrentId();
        Moment moment = Moment.builder()
                .file(file)
                .imageUrls(JSONUtil.toJsonStr(imgs))
                .context(content)
                .userId(currentId)
                .type(type)
                .build();
        boolean save = save(moment);
        if(save){
            String key = RedisConstants.MOMENT_USERID + currentId + ":" + type;

            Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                    .reverseRangeByScoreWithScores(key, 0, Integer.MAX_VALUE, 0, 10);
            if(typedTuples == null || typedTuples.isEmpty()){
                    buildMomentRedis(currentId,type);
            }

            PublicMoment publicMoment = momentMapper.queryOneMoment(moment.getId());
            PublicMomentHash publicMomentHash = BeanUtil.toBean(publicMoment, PublicMomentHash.class);
            stringRedisTemplate.opsForZSet().add(key,JSONUtil.toJsonStr(publicMomentHash),moment.getId());
            stringRedisTemplate.expire(key,RedisConstants.TIME_ONE_DAY, TimeUnit.DAYS);
            return Result.ok();
        }
        return Result.fail("添加失败");
    }


    private void buildMomentRedis(Long id, Integer type) {
        List<PublicMomentHash> publicMomentHashList = momentMapper.BuildMomentRedis(id, type);
        String key = RedisConstants.MOMENT_USERID + id + ":" + type;
        for (PublicMomentHash publicMomentHash : publicMomentHashList) {
            stringRedisTemplate.opsForZSet()
                    .add(key, JSONUtil.toJsonStr(publicMomentHash),
                            publicMomentHash.getId().longValue());
        }
        stringRedisTemplate.expire(key,RedisConstants.TIME_ONE_DAY, TimeUnit.DAYS);
    }

    @Override
    public Result DeleteOrHide(Long id, Integer type) {
        //删除或隐藏或显示,type=0删除，type=2隐藏,type=1显示
        boolean update = lambdaUpdate()
                .eq(Moment::getId, id)
                .set(Moment::getVisibility, type)
                .update();
        if(update){
            Moment moment = getById(id);
            Integer momentType = moment.getType();
            Long currentId = UserHolder.getCurrentId();
            String key = RedisConstants.MOMENT_USERID + currentId + ":" + momentType;
            stringRedisTemplate.delete(key);
            return Result.ok();
        }
        return Result.fail("操作失败");
    }
}
